#pragma once

#include "footage.h"
#include "coordinator.h"
#include "mswin/work_thread.h"
#include "mswin/order_queue.h"
#include "typelib/span.h"

namespace img {
	namespace avi {
		class footage;
		class reader;
	}
}

namespace icache {

struct avi_read_order
{
	span required; ///< this frames will be necessarily read
	span desired;  ///< some of this frames may be not read if it's profitable from the avi's keyframe point of view
	int priority;  ///< same units of measurement as in Win's SetThreadPriorit(...) method
public:
	avi_read_order & unite(const avi_read_order & o) {
		required.unite(o.required);
		desired.unite(o.desired);
		if (priority < o.priority)
			priority = o.priority;
		return * this;
	}
	bool intersecting(const avi_read_order & o) const
		{ return desired.intersecting(o.desired); }
	friend std::ostream & operator << (std::ostream & s, avi_read_order const & o);
};

class avi_read_order_queue : public mswin::OrderQueue<avi_read_order>
{
public:
	bool Push(avi_read_order o);
};

class subscriptions;

class avi_reader : public footage_simple_impl, public mswin::WorkThread<avi_read_order_queue>
{
public:
	typedef mswin::WorkThread<avi_read_order_queue> superclass;
public: //footage inerface
	virtual string filepath() const;
	virtual item_id_sptr frame_id(int frame);
	virtual cached_image_sptr get(int frame, nearby may_be_requested_later = no_nearby, bool wait = true);
	virtual cached_image_sptr try_get(int frame);
	virtual icache::cache * cache() const;
	virtual xml::stream & serialize(xml::stream & s, string const & reference_path);
	virtual string description() const;
	virtual int size() const;
	virtual int first_frame_num() const;
	virtual size_extent dim() const;
	virtual bool alpha_channel() const;
	virtual fraction fps() const;
public:
	avi_reader(const string & filename = string(), icache::coordinator * c = 0, int prefetch = 10);
	virtual ~avi_reader();
	void open(const string & filename);
	void close();
	bool is_open() const;
	void coordinator(icache::coordinator * c);
	icache::coordinator * coordinator() const;
	bool all_keyframes() const;
	int duration() const;
	string const & basename() const;
	string const & fullpath() const;
	int prefetch() const;
	void prefetch(int p);
	std::ostream & log() const;
	void log(std::ostream & l);
	void dont_log();
public:
	//if priority of order is larger than the current thread's priority, then boosts thread's priority
	void request(const OrderType & o);
	void request(int frame, nearby may_be_requested_later, int priority);
	///call this function after an order have placed, the function returns when the requested frame will be in cache
	void wait_for_frame(int frame);
	void request_and_wait(int frame, nearby may_be_requested_later, int priority);
private:
	using superclass::Place;
	virtual void Execute(const OrderType & o);
private:
	friend class inspect_keyframes;
	std::auto_ptr<inspect_keyframes> _inspector;
private:
	///every frame read from AVI will be placed in disc cache;
	///if not memory cache is present, then loaded images, which are waiting for, will be put over there
	icache::coordinator * _coordinator;
	std::auto_ptr<img::avi::footage> _footage;
	std::auto_ptr<img::avi::reader> _reader;
	bool _all_keyframes; ///< true if every frame in the footage is a key frame; in that case
						 ///< disc cache is not used (mem cache becomes required), and prefetch effectively becomes 1
	std::auto_ptr<subscriptions> _subscriptions;
	string _basename, _fullpath; ///< to the footage
	int _prefetch;
	std::ostream * _log; ///< prints here frames' requests and their execution
};

//inlines

inline bool avi_reader::is_open() const
	{ return _reader.get() != 0; }

inline string const & avi_reader::basename() const
	{ return _basename; }

inline string const & avi_reader::fullpath() const
	{ return _fullpath; }

inline int avi_reader::prefetch() const
	{ return _prefetch; }

inline void avi_reader::prefetch(int p)
	{ _prefetch = p; }

inline std::ostream & avi_reader::log() const
	{ return * _log; }

inline void avi_reader::log(std::ostream & l)
	{ _log = &l; }

inline void avi_reader::dont_log()
	{ _log = 0; }

inline icache::coordinator * avi_reader::coordinator() const
	{ return _coordinator; }

} //namespace icache
